#include <iostream>
#include <unistd.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/stat.h>
using std::string;
using std::cin;
using std::cout;
using std::endl;
typedef unsigned int uint;
typedef long long LL;
const int BUF_SIIZE = 1024;
const char* CP_C = "gcc -o Main -Djudge -O2 Main.c";
const char* CP_CPP = "g++ -o Main -Djudge -O2 -std=c++11 Main.cpp";
const char* CP_JAVA = " ";
const char* PROC_STATUS = "/proc/%d/status";

//define the language mark
const int C = 1;
const int CPP = 2;
const int JAVA = 3;
//define judge status
const int OJ_OK = 0;
const int OJ_ML = 1;
const int OJ_TL = 2;
const int OJ_RE = 3;
const int OJ_OL = 4;

LL getFileSize(char* file) {
    struct stat f_stat;
    if (stat(file, &f_stat) == -1) {
        return 0;
    }
    return (LL) f_stat.st_size;
}

//get the process status by read file /proc/[pid]/status
int getProcStatus(int pid, char* contex) {
    char buf[BUF_SIIZE];
    sprintf(buf, PROC_STATUS, pid);
    FILE* proc = fopen(buf, "r");
    if (proc == NULL) return -1;
    int len = strlen(contex);
    int ret = -1;
    while ( fgets(buf, BUF_SIIZE-3, proc) > 0 ) {
        if (strncmp(buf, contex, len) == 0) {
            sscanf(buf+len+1, "%d", &ret);
        }
    }
    fclose(proc);
    return ret;
}

int compile(int lan, char* input) {
    char* dir = (char*)(input+(string)"ce.txt").c_str();
    int pid = fork();
    if (pid == 0) {
        rlimit limit;
        limit.rlim_cur = 10;
        limit.rlim_max = 10;
        setrlimit(RLIMIT_CPU, &limit);
        limit.rlim_cur = (50<<20);
        limit.rlim_max = limit.rlim_cur;
        setrlimit(RLIMIT_FSIZE, &limit);
        limit.rlim_cur = (10<<20);
        limit.rlim_max = limit.rlim_cur;
        setrlimit(RLIMIT_AS, &limit);
        setrlimit(RLIMIT_DATA, &limit);
        freopen(dir, "w", stderr);
        switch(lan) {
            case C:
                system(CP_C);
                break;
            case CPP:
                system(CP_CPP);
                break;
            case JAVA:
                system(CP_JAVA);
                break;
            default:
                return -1;
        }
        exit(0);
    }
    else {
        int status = 0;
        waitpid(pid, &status, 0);
        status = getFileSize(dir);
        return status;
    }
}

int runCode(int lan, char* input, const int& time_lmt, const int& mem_lmt) {
    nice(19);
    freopen(input, "r", stdin);
    freopen(((string)input+".res").c_str(), "w", stdout);
    char* err = (char*)((string)input+".err").c_str();
    freopen(err, "w", stderr);
    rlimit limit;
    limit.rlim_cur = time_lmt + 1;
    limit.rlim_max = limit.rlim_cur;
    setrlimit(RLIMIT_CPU, &limit);
    limit.rlim_cur = (1<<20);
    limit.rlim_max = limit.rlim_cur;
    setrlimit(RLIMIT_FSIZE, &limit);
    limit.rlim_cur = 16;
    limit.rlim_max = 16;
    setrlimit(RLIMIT_NPROC, &limit);
    limit.rlim_cur = ((mem_lmt*2)<<20);
    limit.rlim_max = limit.rlim_cur;
    setrlimit(RLIMIT_AS, &limit);
    setrlimit(RLIMIT_DATA, &limit);
    setrlimit(RLIMIT_STACK, &limit);
    switch(lan) {
        case C:
        case CPP:
            system("./Main");
            break;
        case JAVA:
            system("/usr/bin/java ./Main");
            break;
    }
    exit(getFileSize(err));
}

int watchRun(int lan, char* input, int pid, const int& time_lmt,
        const int& mem_lmt, double& time, double& mem) {
    int tmem, mmem, status;
    int ret = OJ_OK;
    rusage rusa;
    tmem = (getProcStatus(pid, (char*)"VmRSS:")<<10);
    while (1) {
        wait4(pid, &status, 0, &rusa);
        if (lan = JAVA) {

        }else {
            tmem = (getProcStatus(pid, (char*)"VmPeak:")<<10);
        }
        if (tmem > mmem) mmem = tmem;
        if (tmem > (mem_lmt<<20)) {
            ret = OJ_ML;
            break;
        }
        if (WIFEXITED(status)) {
            int t = WEXITSTATUS(status);
            if (t>0) ret = OJ_RE;
            break;
        }
        if (WIFSIGNALED(status)) {
            int t = WTERMSIG(status);
            switch (t) {
                case SIGCHLD:
                case SIGALRM:
                    alarm(0);
                case SIGKILL:
                case SIGXCPU:
                    ret = OJ_TL;
                    break;
                case SIGXFSZ:
                    ret = OJ_OL;
                    break;
                default:
                    ret = OJ_RE;
            }
            break;
        }
    }
    mem = (double)mmem/1000000.0;
    time = ((double)rusa.ru_utime.tv_sec * 1000.0 + (double)rusa.ru_utime.tv_usec / 1000.0);
    time += ((double)rusa.ru_stime.tv_sec * 1000.0 + (double)rusa.ru_stime.tv_usec / 1000.0);
    if (time > time_lmt) ret = OJ_TL;
    return ret;
}

int main(int argc, char** argv) {
    int compile_ok = compile(CPP, (char*)"./");
    double time, mem;
    if (compile_ok == 0) {
        int pid = fork();
        if (pid == 0) {
            runCode(CPP,(char*)"./", 1000, 10);
        }else {
            watchRun(CPP, (char*)"./", pid, 1000, 10, time, mem);
        }
    }
    else return -1;
    printf("Time: %lf\nMemery: %lf\n", time, mem);
    return 0;
}
